package com.jimmyhsu.ecnudaowei.coursetable.face;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.support.annotation.Nullable;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v4.content.ContextCompat;
import android.util.Base64;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.jimmyhsu.ecnudaowei.R;
import com.zhy.http.okhttp.utils.ImageUtils;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;

public class FaceDetectFragment extends Fragment implements TextureView.SurfaceTextureListener,
        ImageReader.OnImageAvailableListener, SurfaceHolder.Callback {
    public static final String TAG = "FaceDetectFragment";
    private static final SparseIntArray ORIENTATIONS = new SparseIntArray();

    ///为了使照片竖直显示
    static {
        ORIENTATIONS.append(Surface.ROTATION_0, 90);
        ORIENTATIONS.append(Surface.ROTATION_90, 0);
        ORIENTATIONS.append(Surface.ROTATION_180, 270);
        ORIENTATIONS.append(Surface.ROTATION_270, 180);
    }

    private TextureView mPreviewView;
    private SurfaceView mSurfaceView;
    private FloatingActionButton mShotButton;
    private SurfaceHolder mHolder;

    private volatile boolean isSurfaceAvailable = false;
    private int mBitmapWidth;
    private int mBitmapHeight;

    private Handler mHandler;
    private Handler mImageHandler;
    private HandlerThread mThreadHandler;

    private Size mPreviewSize;
    private CaptureRequest.Builder mPreviewBuilder;
    private CameraCaptureSession mSession;
    private ImageReader mImageReader;

    public static FaceDetectFragment newInstance() {
        return new FaceDetectFragment();
    }

    @SuppressWarnings("ResourceType")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.frag_face_detect, container, false);
        initLooper();
        initUIAndListener(v);
        return v;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        EventBus.getDefault().register(this);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        EventBus.getDefault().unregister(this);
    }

    private void initLooper() {
        mThreadHandler = new HandlerThread("CAMERA2");
        mThreadHandler.start();
        mHandler = new Handler(mThreadHandler.getLooper());
        mImageHandler = new Handler(Looper.getMainLooper());
    }

    private void initUIAndListener(View v) {
        mPreviewView = (TextureView) v.findViewById(R.id.id_face_preview);
        mPreviewView.setSurfaceTextureListener(this);
        mSurfaceView = (SurfaceView) v.findViewById(R.id.id_face_face_frame);
        mHolder = mSurfaceView.getHolder();
        mSurfaceView.setZOrderOnTop(true);
        mHolder.setFormat(PixelFormat.TRANSPARENT);
        mHolder.addCallback(this);
        mShotButton = (FloatingActionButton) v.findViewById(R.id.id_face_shot);
        mShotButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                takePicture();
            }
        });
    }

    @SuppressWarnings("ResourceType")
    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try {
            //获得CameraManager
            CameraManager cameraManager = (CameraManager) getActivity().getSystemService(Context.CAMERA_SERVICE);
            //获得属性
            CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics("0");
            //支持的STREAM CONFIGURATION
            StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            //显示的size
            Size[] outputSizes = map.getOutputSizes(SurfaceTexture.class);
            mPreviewSize = getCloselyPreSize(width, height, outputSizes);
            mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(),
                    ImageFormat.JPEG, 1);
            mImageReader.setOnImageAvailableListener(FaceDetectFragment.this, mImageHandler);
//            Log.e("camera", "choose size: " + mPreviewSize.getWidth() + ", " + mPreviewSize.getHeight());
            cameraManager.openCamera("0", mCameraDeviceStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (mSession != null && mDevice != null) {
            try {
                mSession.stopRepeating();
                mSession.close();
                mDevice.close();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }

        }
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    }

    private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            try {
                startPreview(camera);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
        }

        @Override
        public void onError(CameraDevice camera, int error) {
        }
    };
    //开始预览，主要是camera.createCaptureSession这段代码很重要，创建会话
    private CameraDevice mDevice;

    private void startPreview(CameraDevice camera) throws CameraAccessException {
        if (mDevice == null) mDevice = camera;
        SurfaceTexture texture = mPreviewView.getSurfaceTexture();
        texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        Surface surface = new Surface(texture);
        try {
            mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        mPreviewBuilder.addTarget(surface);
//        mPreviewBuilder.addTarget(mImageReader.getSurface());
        camera.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionStateCallback, mHandler);
    }

    private void takePicture() {
        if (mDevice == null) return;
        // 创建拍照需要的CaptureRequest.Builder
        final CaptureRequest.Builder captureRequestBuilder;
        try {
            captureRequestBuilder = mDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            // 将imageReader的surface作为CaptureRequest.Builder的目标
            captureRequestBuilder.addTarget(mImageReader.getSurface());
            // 自动对焦
            captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 自动曝光
            captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
            // 根据设备方向计算设置照片的方向
            captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
            //拍照
            CaptureRequest mCaptureRequest = captureRequestBuilder.build();
            mSession.capture(mCaptureRequest, null, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
        @Override
        public void onConfigured(CameraCaptureSession session) {
            try {
                updatePreview(session);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(CameraCaptureSession session) {
        }
    };

    private void updatePreview(CameraCaptureSession session) throws CameraAccessException {
        if (mSession == null) mSession = session;
        session.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
    }

    protected static Size getCloselyPreSize(int textureWidth, int textureHeight,
                                            Size[] preSizes) {

        int ReqTmpWidth;
        int ReqTmpHeight;
        // 当屏幕为垂直的时候需要把宽高值进行调换，保证宽大于高

        ReqTmpWidth = textureHeight;
        ReqTmpHeight = textureWidth;

        //先查找preview中是否存在与surfaceview相同宽高的尺寸
        for (Size size : preSizes) {
            if ((size.getWidth() == ReqTmpWidth) && (size.getHeight() == ReqTmpHeight)) {
                return size;
            }
        }

        // 得到与传入的宽高比最接近的size
        float reqRatio = ((float) ReqTmpWidth) / ReqTmpHeight;
        float curRatio, deltaRatio;
        float deltaRatioMin = Float.MAX_VALUE;
        Size retSize = null;
        for (Size size : preSizes) {
            curRatio = ((float) size.getWidth()) / size.getHeight();
            deltaRatio = Math.abs(reqRatio - curRatio);
            if (deltaRatio < deltaRatioMin) {
                deltaRatioMin = deltaRatio;
                retSize = size;
            }
        }

        return retSize;
    }

    @Override
    public void onImageAvailable(ImageReader reader) {
//        EventBus.getDefault().post(reader);
        mDevice.close();
        mDevice = null;
        // 拿到拍照照片数据
        Image image = reader.acquireNextImage();
        ByteBuffer buffer = image.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[buffer.remaining()];
        buffer.get(bytes);//由缓冲区存入字节数组
        final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        mBitmapWidth = bitmap.getWidth();
        mBitmapHeight = bitmap.getHeight();
        if (bitmap != null) {
            EventBus.getDefault().post(bitmap);
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        isSurfaceAvailable = true;
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        isSurfaceAvailable = false;
    }

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void drawMatchedFrame(final Face face) {
        if (mHolder == null || !isSurfaceAvailable) return;
        Canvas canvas = null;
        try {
            canvas = mHolder.lockCanvas();
            int targetWidth = mPreviewView.getMeasuredWidth();
            int targetHeight = mPreviewView.getMeasuredHeight();
            float widthRatio = targetWidth * 1.0f / mBitmapWidth;
            float heightRatio = targetHeight * 1.0f / mBitmapHeight;

            Paint paint = new Paint();
            paint.setColor(ContextCompat.getColor(getContext(), android.R.color.holo_green_dark));
            paint.setStyle(Paint.Style.STROKE);
            paint.setStrokeWidth(2f);

            Rect rect = new Rect((int) (face.getLeft() * widthRatio),
                    (int) (face.getTop() * heightRatio),
                    (int) ((face.getLeft() + face.getWidth()) * widthRatio),
                    (int) ((face.getTop() + face.getHeight()) * heightRatio));
            canvas.drawRect(rect, paint);

        } catch (Exception e) {

        } finally {
            if (canvas != null) {
                mHolder.unlockCanvasAndPost(canvas);
            }
        }
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void drawFaceFrame(final List<Face> faces) {
        if (mHolder == null || !isSurfaceAvailable) return;
        new Thread(new Runnable() {
            private Canvas mCanvas;

            @Override
            public void run() {
                try {
                    mCanvas = mHolder.lockCanvas();
                    int targetWidth = mPreviewView.getMeasuredWidth();
                    int targetHeight = mPreviewView.getMeasuredHeight();
                    float widthRatio = targetWidth * 1.0f / mBitmapWidth;
                    float heightRatio = targetHeight * 1.0f / mBitmapHeight;

                    Paint paint = new Paint();
                    paint.setColor(ContextCompat.getColor(getContext(), R.color.colorPrimary));
                    paint.setStyle(Paint.Style.STROKE);
                    paint.setStrokeWidth(2f);
                    mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); //清楚掉上一次的画框。
                    for (Face face : faces) {
                        Rect rect = new Rect((int) (face.getLeft() * widthRatio),
                                (int) (face.getTop() * heightRatio),
                                (int) ((face.getLeft() + face.getWidth()) * widthRatio),
                                (int) ((face.getTop() + face.getHeight()) * heightRatio));
                        mCanvas.drawRect(rect, paint);
                    }
                } catch (Exception e) {

                } finally {
                    if (mCanvas != null) {
                        mHolder.unlockCanvasAndPost(mCanvas);
                    }
                }

            }
        }).start();

    }
}